home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-02 / tprint.zip / PRINTER.DOC < prev    next >
Text File  |  1991-05-21  |  50KB  |  731 lines

  1.                              DOCUMENTATION FOR TURBO PASCAL PRINTER UNITS
  2.  
  3.         These units are for use in Turbo Pascal Windows Programs.  They are shareware.  You may use
  4. these units in any non-commercial products freely.  You may copy the programs and the documentation
  5. and distribute them freely.  If these units are to be incorporated into a commercial product, or if you wish
  6. source on disk, please send $25.00 to OptiCom, Inc, Box 16-1127, Miami, FL 33116.  Our phone
  7. number is (305) 271-5138, and our CompuServe ID is 71250,71.  If you register the product, I will send
  8. you irregular updates of these objects as I improve them.
  9.  
  10.         These objects, all source code and the contents of this document are copyright (c) 1991 OptiCom,
  11. Inc.  We would sincerely welcome any comments/criticism/ideas you might have about these objects.
  12.  
  13.         The idea for these objects and the information in this document came from several sources.  The
  14. objects themselves are loosely based on the printer classes contained in the Actor Development
  15. Environment, an excellent product of the Whitewater Group.  Other material came from Charles Petzold's
  16. Programming Windows, the Microsoft SDK, the Turbo Windows Manuals, Compuserve and several
  17. friends and associates.
  18.  
  19. Files on this disk:
  20.         pDevice.pas            Formal printer object, tPrnDevice.
  21.         printer.pas            Printer Object, tPrinter.
  22.         repPrn.pas             example of a report printer derived from the printer class.
  23.         testPrn1.pas           example file printer program, using the tPrinter object.
  24.         testPrn2.pas           example file printer program, using the reportPrinter object.
  25.         test.res               resource file
  26.         prt.res                resurce file
  27.         printer.doc            This document
  28.  
  29.  
  30. GENERAL OVERVIEW
  31.         These objects are designed to provide an interface between a printer and an application running
  32. under Windows.  The structure of these objects is such that you should not have to modify them to add
  33. functionality or change their behavior.  You should create descendants of the objects to add additional
  34. features.  An example of this is the ReportPrinter object, in the file RepPrn.pas.  The reportPrinter object
  35. adds several features to the printer unit to handle headings and page size.  Having learned the basics of
  36. OOP in the Actor environment, I have come to appreciate the advantages of adhering as strictly as
  37. possible to the principles of OOP.  The Actor environment is pure OOP.  That means that everything
  38. (and I mean everything!) is an object.  The main advantage of OOP programming is the ease of
  39. modifying and extending the objects that you create.  I hope these objects will prove to be good examples
  40. of this type of programming.
  41.  
  42.         These objects have been written with the OOP philosophy in mind.  There is no access to the
  43. instance variables of an object outside that object's scope.  All access to instance variables is through the
  44. methods of the object.  Modifications to be made to the objects are made through descendants, not by
  45. modifying the code of the object.  This is illustrated by the ReportPrinter object.
  46.  
  47.         This document is divided into several sections.  The first discusses the relationship between
  48. Windows and the printer.  It covers, in a general manner, the calls to Windows contained in the objects. 
  49. I have tried to provide enough information to make clear what I am doing in each object, and so that the
  50. objects can be extended to suit your own needs.  The other sections discuss the objects themselves and
  51. the sample programs.  Each object section contains an explanation of the declared types in the unit, the
  52. object's instance variables and methods, and a bit about what the object is supposed to do.  The sections
  53. on the sample programs discuss the nature of the programs, and how they use the objects to print.
  54.  
  55. PRINTING IN WINDOWS
  56.         It used to be that all you had to do to print something was USE the printer, and let fly.  With
  57. some well placed WriteLn statements and judicious spacing, you could produce reasonable output.  Not
  58. any more.  Under Windows, printing requires almost as much programming as some small applications! 
  59.  
  60.         To a Windows application, sending output to a printer is not much different than sending output
  61. to a window.  The application actually sends its output to Windows, and Windows handles the mechanism
  62. of getting it where it is supposed to go.  In general, printing under Windows requires the following steps:
  63.         1.      The application must retrieve information about the current printer from the WIN.INI file.
  64.         2.      The application must create a device context (a handle) for the printer.
  65.         3.      Output is sent to the device context.  
  66.         4.      The application communicates with the printer device driver using escape codes.
  67.         5.      Windows activates the print spooler to manage the print job.
  68.  
  69.         In addition to these basic steps, an application can also display a dialog box to allow a user to
  70. cancel a print job that is being spooled.  This requires the instantiation of an Abort procedure to notify
  71. windows about the status of the print job.
  72.  
  73.         Windows' management of each print job revolves around the concept of a document.  You can
  74. think of each, complete print job as a document.  This applies no matter how large the output will be. 
  75. Each time a new print job is started, that print job is one document.  Since it is possible that more that
  76. one document may be printing at the same time, each individual print job must be kept separate from any
  77. other print job to avoid mixing them up during output.  The application must notify Windows when a
  78. document is going to start. When printing has finished, the application must notify Windows that the
  79. document has ended.  This notification is done using escape calls.
  80.  
  81.         In the following section, some of the concepts introduced above will be discussed in greater
  82. detail.
  83.  
  84. ABORT PROCEDURE:  An abort procedure is known as a callback function.  It is a function in an
  85. application that is called by Windows during a print job.  The function returns a boolean value indicating
  86. whether the user has canceled the print job.  During normal processing, messages sent to the abort
  87. function are translated and dispatched back to Windows.  As long as the print job is not canceled, the
  88. function returns a TRUE value to Windows.  If the print job is canceled, the function returns FALSE,
  89. letting Windows know the job was canceled.
  90.  
  91.         In order to implement an abort procedure, it is necessary to provide a dialog box and an abort
  92. function.  The dialog box generally contains a brief message ('Sending job to spooler...') and a cancel
  93. button.  Pressing the cancel button should set a variable accessible to the abort function to FALSE.  The
  94. abort procedure must be declared as a EXPORTed function, and must return a boolean value.  This is
  95. done in the AbortProc method of the printer object.  The abort function may not be a method of an
  96. object, since object methods may not be exported.  This also means that the print dialog may not be
  97. contained in an instance variable of an object, since the Abort procedure would not have access to it.
  98. Finally, the variable set by the Cancel method of the print dialog must be global to the unit in which these
  99. two items are declared.  Again, the process necessary for creating an abort mechanism may be seen in
  100. the Printer.pas file.
  101.  
  102.         The next step is to notify Windows of the existence of the abort function.  This is done by
  103. sending the SETABORTPROC escape code to Windows, with a far pointer to the abort function as a
  104. parameter.  This pointer is obtained through the MakeProcInstance method.  MakeProcInstance creates
  105. a far pointer that is bound to the data segment of the instance of the application that contains the abort
  106. function.  This is so that when Windows calls the abort function, it uses data in the current instance.  The
  107. application instance refers to the instance handle of the current application.  A handle is a number that
  108. uniquely identifies a program running under Windows.  Since multiple instances of a program share code,
  109. it is necessary for Windows to keep track of who is doing what.  The call to MakeProcInstance returns
  110. a tFarPointer which is then used in the SetAbortProc escape call.   The SetAbortProc escape call passes
  111. the pointer to the abort function to Windows.
  112.  
  113.         If an application does not set an abort procedure, it is quite likely that any errors will cause the
  114. program to crash.  One common error is a lack of disk space for spooling.  In the abort procedure, the
  115. application can decide whether to wait for some disk space to be freed, or to cancel the print job.  If an
  116. print job terminates abnormally, or if it is canceled, the GDI automatically cancels the print job.  In this
  117. case, the application should not attempt to end the print job using escape calls.
  118.  
  119.         Both the abort procedure and the print dialog box must be instantiated prior to issuing the
  120. STARTDOC escape call to create the document. 
  121.  
  122. DEVICE CONTEXT: When an application wants to use a device, it must obtain a handle for that device
  123. from Windows.  This handle is known as the device context.  Through the device context, an application
  124. has access to various devices attached to the computer.  The device context is actually a data structure
  125. maintained by the GDI, containing information about the particular device to which it belongs.  For
  126. example, when output is sent to the printer, the GDI uses the information contained in this data structure
  127. to properly format the output.  A device context works very much like a file in a Pascal program.  Once
  128. you have assigned a file name to a file variable, all access to the file passes through the file variable. 
  129. In a Windows program, all access to the device must 'pass through' the device context handle.
  130.  
  131. Device Mode:  The device mode function is part of the printer device driver.  These drivers are supplied
  132. by the manufacturer and distributed with Windows.  When you call DeviceMode, you are calling a
  133. routine in a DLL.  The deviceMode call is responsible for displaying the printer dialog box, allowing a
  134. user to change the printer parameters.  The name of the function in a driver written for Windows 3 is
  135. ExtDeviceMode.   In drivers for earlier versions of Windows, the function is DeviceMode.  There are
  136. several differences between these two functions.  The most important one is that in the DeviceMode call,
  137. any changes to the device parameters are reflected in the WIN.INI file.  These changes will affect all
  138. current and future sessions of Windows.  In the ExtDeviceMode procedure, the programmer has the
  139. option to make the changes permanent, or to maintain them for the current session only. 
  140.  
  141. ESCAPE CALLS:  Escape calls allow an application to communicate with a printer device driver.  The
  142. escape sequences contain commands and information for the driver.  They can also be used to obtain
  143. device-specific information.   There are quite a number of escape codes that can be used (57, I believe). 
  144. These are listed in the Turbo Windows Reference guide under Printer Escape Codes.
  145.  
  146. GDI (Graphics Device Interface): The GDI is a programming language that acts as an interface between
  147. an application, the printer device driver and the print manager.   An application does not have to know
  148. anything about the devices with which it will communicate.  As long as the application uses the GDI to
  149. access the devices, all of the output functions will be handled by Windows.  This is known as device-
  150. independence.  As you will see, close communication is necessary between an application, Windows and
  151. the printer driver to print a document.
  152.  
  153. WIN.INI file:  The WIN.INI file plays an important part in the printing process.  In the method
  154. GetPrinterParms, this file is queried to obtain information about the current printer.  Information in the
  155. WIN.INI file is stored under application name and key name headings. Printer information is contained
  156. under the application name '[windows]' and the key name 'device'.  The key name is followed by the
  157. currently selected printer, the name of it's device driver, and the port to which the printer is connected. 
  158. For example, a typical device keyword might look like this:
  159.  
  160.                                  Device=IBM Proprinters,PROPRINT,LPT1:
  161.  
  162.         This tells us that the currently selected printer is an IBM Proprinter, the driver is
  163. PROPRINT.DRV, and the printer is connected to LPT1. The settings in this file can be modified by the
  164. DeviceMode call to the printer device driver.  It is also possible, using the ClearEnv method, to force
  165. a device to read the WIN.INI file to obtain the current printer parameters.
  166.  
  167.  
  168. ===============================================
  169.                                        PRINTER OBJECT REFERENCE
  170.  
  171. tPrnDevice, Contained in file pDevice.Pas
  172.  
  173. INSTANCE VARIABLES
  174.  
  175. hPrintDC        Holds the printer device context.  This variable is instantiated by the DCcreated method.
  176.  
  177. hWindow         Contains the handle to the parent window from which the printer object was created. 
  178.                 This variable is instantiated by the Init method, using a value passed from the application.
  179.  
  180. docName         The name of the document that is being printed.  This variable is instantiated in the
  181.                 BeginDoc method.  The document name is used with the STARTDOC escape call to
  182.                 uniquely identify the document being printed.  This allows the print manager to manage
  183.                 separate print jobs, without their interfering with one another.  When the print manager
  184.                 is active, this name appears on the status line for the selected printer.
  185.  
  186. Device          The name of the printer. This is obtained through a call to getPrinterParms, which
  187.                 retrieves the information from the WIN.INI file
  188.  
  189. Driver          The name of the device driver associated with the device.  This information is also
  190.                 obtained through the getPrinterParms call
  191.  
  192. dMode           a tDevMode data structure.  Although it is not used extensively in these objects,  this
  193.                 structure holds a great deal of information about the printer and device driver.  It is used
  194.                 by DeviceCapabilities and by the getExtDeviceMode to hold information about a printer
  195.                 device driver.
  196.  
  197. noSpooler       A boolean variable that is set in the GetPrinterParms method, based on the value for
  198.                 spooler in the WIN.INI file.  It indicates whether the spooler is active or not.
  199.  
  200. prnPort         The port to which the object will print.  This information is obtained from the WIN.INI
  201.                 file through a call to getPrinterParms.
  202.  
  203.  
  204. TYPES
  205.  
  206. devArray        An 80 character string used in the unit to hold various values.
  207.  
  208. prnErrors       Enumerated type defining the different errors that can occur during the printing process. 
  209.                 These are used as parameters when calling the PrnError method.
  210.  
  211. pPrnDevice      A pointer to the tPrnDevice Object.
  212.  
  213. tPrnDevice      The formal printer object.  You should not create an instance of this class.  Instantiate
  214.                 tPrinter instead.
  215.  
  216. tGetDevMode     This is a template for a windows function call to display the device driver dialog box for
  217.                 drivers not written for windows 3.0.
  218.  
  219. tGetExtDevMode         This is a template for a windows function call to display the device driver dialog
  220.                 box for device drivers written for windows 3.0.
  221.  
  222.  
  223.  
  224. METHODS
  225.  
  226. AbortPrn        Aborts the current print job.
  227.  
  228. BeginDoc        Sends the STARTDOC escape call to windows to indicate the start of a document.  This
  229.                 is an important call. Included in the Escape call is the name of the document to be
  230.                 printed.  This name is used by the GDI to identify a unique print job, and prevents
  231.                 documents longer than one page from being mixed in with other documents.  The job
  232.                 name appears in the print manager list of jobs.  The STARTDOC call should always be
  233.                 paired with an ENDDOC or ABORTDOC escape call.
  234.  
  235. CallEscape      Sends an escape code to the GDI, using passed parameters. The syntax of an Escape call
  236.                 is:
  237.                        Escape(DC: hDC; Escape, Count: Integer; InData, OutData: Pointer): Integer
  238.  
  239.                 where DC is the selected device context, Escape is the escape code, count is the number
  240.                 of bytes in InData, and outData is the data structure to receive data from the escape call,
  241.                 if requested.  The function returns an integer value that is positive if the escape call was
  242.                 successful, zero if escape is not implemented, and negative if an error occurred.  This
  243.                 method centralizes the escape calls to allow errors resulting from the calls to be handled
  244.                 from one location.  Most errors occur on a NewFrame boundary, which signifies the end
  245.                 of a page.
  246.  
  247. ClearEnv        Clears the device's environment.  This forces the printer driver to read the current printer
  248.                 settings from the WIN.INI file.
  249.  
  250. DCcreated       Creates a device context for the printer by calling the CreateDC method.   CreateDC
  251.                 returns a word value greater than 0 if the call was successful.
  252.  
  253. DeleteContext   Deletes the device context created for the printer.
  254.  
  255. DeviceName      Returns the value of the Device instance variable.
  256.  
  257. DocAbort        Sends the ABORTDOC escape code.  This code terminates the current job and erases
  258.                 everything the application has written to the device since the last ENDDOC code.  This
  259.                 code is normally used to terminate print jobs that have not implemented an abort
  260.                 procedure, or those that have not reached their first NewFrame escape call.  If a print
  261.                 job terminates due to cancellation or an error, this call should not be used.  The GDI
  262.                 automatically terminates print jobs under these circumstances.  If the application has
  263.                 created a cancellation dialog, the ABORTDOC code must be sent before the dialog box
  264.                 is destroyed, and before freeing an abort procedure-instance.
  265.  
  266. Done            Destructor method.  Calls the parent (tObject) destructor.
  267.  
  268. DoNewFrame      Sends the NEWFRAME escape code to Windows. This code lets the printer know that
  269.                 the application has finished writing to a page.  It causes a form feed to be issued.  The
  270.                 NEWFRAME call restores the default values of the device context.  If a font other than
  271.                 the default font has been selected, that font must be reselected after the NEWFRAME
  272.                 Escape.
  273.  
  274. DraftModeOff    Turns the printer draft mode off by sending the DRAFTMODE escape code.  This
  275.                 method illustrates the use of the InData structure, sending a word value indicating the
  276.                 desired state of the draft mode.  The value is passed in the high portion of the word. A
  277.                 value of 0 turns draft mode off, 01 turns it on. The draft mode of the printer can only
  278.                 be changed at page boundaries, such as after a NEWFRAME escape call.  
  279.  
  280. DraftModeOn     Turns the printer draft mode on by sending the DRAFTMODE escape code.  This
  281.                 method illustrates the use of the InData structure, sending a word value indicating the
  282.                 desired state of the draft mode.  The value is passed in the high portion of the word.  A
  283.                 value of 0 turns draft mode off, 01 turns it on.  Since the value is passed in the high
  284.                 portion of the word, the value passed is 1000.  In the method draftModeOff, the passed
  285.                 value is zero.  The draft mode of the printer can only be changed at page boundaries,
  286.                 such as after a NEWFRAME escape call.
  287.  
  288. DriverName      Returns the value of the Driver instance variable.
  289.  
  290. EndDocument     Sends the ENDDOC Escape code to Windows.  This signals that the current document
  291.                 has completed. If the print job has terminated abnormally, this method should not be
  292.                 called.  In cases of abnormal termination, the GDI terminates the operation.  If the
  293.                 application displays a print cancel dialog, the ENDDOC code must be sent before the
  294.                 dialog box is destroyed, and before freeing the procedure instance of an abort procedure.
  295.                 The ENDDOC or ABORTDOC escape calls should always be paired with the
  296.                 STARTDOC escape call.
  297.  
  298. EndOfFile       Ends the document. Causes a newPage, sends the ESCAPEDOC escape call, and deletes
  299.                 the printer device context.
  300.  
  301. FlushPrn        Sends the FLUSHOUTPUT escape code, which flushes the printer's output buffer.
  302.  
  303. GetPrinterParms        Retrieves the printer parameters from the WIN.INI file.  The syntax for the call
  304.                 to GetProfileString is:
  305.  
  306.                 GetProfileString(appName,keyName,default,RetunString: pChar; Size: Integer)
  307.  
  308.                 where appName is the heading name to search for in the WIN.INI file, keyName is the
  309.                 key to search for under the heading, default is the default value if keyName is not found,
  310.                 ReturnString is the buffer in which the value of KeyName will be stored, and Size is the
  311.                 size of the buffer.  The appName is usually 'windows', the keyName is usually 'device'.
  312.  
  313.                        On returning from the call, ReturnString contains the name of the printer, the
  314.                 name of the printer driver and the port to which the printer is connected.  These values
  315.                 are separated by commas.  The returned values are copied to instance variables, since
  316.                 they are used by other methods in the object. 
  317.  
  318.                        Finally, there is another call to GetProfileString, this time searching for the
  319.                 keyName, 'spooler'. The value at this location is then used to set the boolean variable,
  320.                 noSpooler to true or false.  This variable indicates whether the print spooler is active.
  321.  
  322. Init            Constructor method.  Calls the parent (tObject) constructor.
  323.  
  324. InitAbortProc   Sends the SETABORTPROC escape code to initialize the abort procedure.  The address
  325.                 for this procedure is passed in the procAddr variable.  This illustrates the use of the
  326.                 InData data structure to send information to Windows.  See the discussion about abort
  327.                 procedures, above, for more information.
  328.  
  329. okPrint         Returns true if the device context is greater than zero.
  330.  
  331. PrinterPort     Returns the value of the prnPort instance variable.
  332.  
  333. prnDeviceMode          This method displays the printer device driver dialog box, allowing the user to
  334.                 change the printer settings.  This method has several variables: 
  335.  
  336.                        dHandle         Handle of the load library for the current printer.
  337.                        drvName         File name of the driver used to get dHandle.
  338.                        pAddr           address of the procedure in the device DLL that will be called.
  339.  
  340.                        A call to GetPrinterParms initializes the variables device and driver.  Using the
  341.                 value in driver, a filename for the device driver is created by concatenating the extension
  342.                 '.drv' to the driver name.  This is stored in the local variable, drvName.  The call to
  343.                 LoadLibrary loads the device driver into memory, and returns a tHandle value that
  344.                 specifies the library module instance.  If the value is less than 32, an error occurred. 
  345.                 Note that in this version of the printer object, I am not trapping errors resulting from this
  346.                 call.  The first call to GetProcAddress searches the DLL for a function named
  347.                 ExtDeviceMode.  If that function is located, a tFarProc value is returned, representing
  348.                 the address of the function in the DLL.  This function is present only in device drivers
  349.                 written for windows 3.0.  If an address is returned, the ExtDeviceMode function is
  350.                 called, using the tGetExtDevMode function type as a template.  The syntax for a call to
  351.                 ExtDeviceMode is:
  352.  
  353.                        ExtDevMode(hWnd,hDriver,lpDevModeOutput,lpDeviceName,
  354.                                        lpPort,lpDevModeInput,lpProfile,wMode)
  355.  
  356.                 where hWnd is the parent window of the caller, hDriver is the device driver module,
  357.                 lpDevModeOutput is a tDevMode data structure.  The driver will write the initialization
  358.                 information supplied in the lpDevModeInput parameter to this structure. lpDeviceName
  359.                 is the name of the printer device, lpPort is the port to which the printer is connected,
  360.                 lpDevModeInput is a tDevMode structure that supplies initialization information to the
  361.                 printer driver, lpProfile contains the name of a file with initialization information.  If this
  362.                 parameter is nil, WIN.INI is used.  wMode is a mask that determines what types of
  363.                 operations the function will perform.  If wMode is 0, the call to the function returns the
  364.                 number of bytes required by the printer device driver structure. Otherwise, this parameter
  365.                 must have one or more of the following values (multiple values should be OR'd
  366.                 together):
  367.  
  368.                        dm_Copy         Writes the printer driver settings to the tDevMode record passed
  369.                                        in lpDevModeOutput.
  370.  
  371.                        dm_Modify       Modifies the printer driver settings to the values passed in the
  372.                                        lpDevModeInput record.
  373.  
  374.                        dm_Prompt       Displays the device mode dialog box, and changes the printer
  375.                                        settings to what the user specified
  376.  
  377.                        dm_Update       Updates the printer environment and the WIN.INI to reflect the
  378.                                        changes made by the user to the printer settings.
  379.  
  380.                        If you do not include dm_update in the call to DevMode, any changes made to
  381.                 the printer settings will be for the current session only, and will not affect other,
  382.                 concurrent sessions.
  383.  
  384.                        If the value returned from the first call to GetProcAddress is nil, indicating that
  385.                 the driver was not written for Windows 3, a second call is made to GetProcAddress to
  386.                 get the address of the DeviceMode function.  The DeviceMode function always updates
  387.                 the WIN.INI file when it's parameters are changed.  The syntax for a call to DeviceMode
  388.                 is: 
  389.                                DeviceMode()
  390.                 where
  391.  
  392.  
  393.                        The call to FreeLibrary releases the loaded library module, and any memory
  394.                 associated with it.
  395.  
  396. PrnError        Calls an error routine based on the value of msgNum.  Each of the error routines has
  397.                 been coded separately to allow them to be overridden in descendant objects.  The error
  398.                 routines are not listed here.  Each one calls the messageBox routine, passing the address
  399.                 of the appropriate error string and dialog title.  These strings are declared as constants
  400.                 (eMsg) at the start of the implementation section.
  401.  
  402.  
  403. USING TPRNDEVICE
  404.  
  405.         The tPrnDevice object is a formal object; that is, you should never create an instance of it.  It's
  406. purpose is to manage the lower level interface between Windows, an application, and the device driver. 
  407. The tPrinter object, discussed next should be instantiated instead.  It provides the higher level interface
  408. between the application and the tPrnDevice object.
  409.  
  410.  
  411.  
  412. THE TPRINTER OBJECT
  413.  
  414. Instance Variables
  415. HInst           Contains the application instance value passed from the calling application.  This variable
  416.                 is used in the NewAbortProc method when the call to MakeProcInstance is made.  See
  417.                 the discussion concerning abort procedures at the beginning of this document.
  418.  
  419. LpAbortProc     Receives the tFarProc value returned from the MakeProcInstance call in the
  420.                 NewAbortProc method.
  421.  
  422. maxX            Holds the maximum line length, in pixels.  This variable is initialized in the Start
  423.                 method.
  424.  
  425. maxY            Holds the maximum page length, in pixels. This variable is initialized in the Start
  426.                 method.
  427.  
  428. Metrics         A tTextMetrics data structure.  This structure contains information about the selected
  429.                 font.  It include the average dimensions of the characters, the number of characters in the
  430.                 font and the character set on which the font is based. Text metrics are most often used
  431.                 to determine the line spacing of the printed output.  In this unit, the height of a line is
  432.                 returned by the height method. The height is the sum of the tmHeight field, which is the
  433.                 height of each character cell, and the tmExternalLeading fields, which is the
  434.                 recommended spacing between the bottom of one character cell and the top of the next.
  435.  
  436. OkToPrint       This variable is used as a flag to determine whether various steps in the initialization
  437.                 process were completed successfully.
  438.  
  439. posX            Holds the horizontal position of the print head on a line, in pixels.
  440.  
  441. posY            Holds the vertical position  of the print head on the page, in pixels.
  442.  
  443. TheParent       A pWindowsObject that is initialized in the Init method.  This variable holds the parent
  444.                 window from which this routine was instantiated.  It's value is used in the printDialog
  445.                 method to instantiate the print cancel dialog.
  446.  
  447. TYPES
  448.  
  449. tPrinter        The printer object.  This is the lowest-level object that should be instantiated to print
  450.                 under Windows.  It is a descendant of the tPrnDevice object, which handles the lower
  451.                 level interface with Windows.
  452.  
  453. tPrnDialog      A descendant of tDialog.  This is the print cancel dialog that appears when a print job
  454.                 is being spooled.  Pressing the cancel button sets the unit variable UserAbort to true,
  455.                 causing the print job to be canceled.
  456.  
  457.  
  458. UNIT VARIABLES
  459.  
  460. PrintDialog     A pointer to a tPrnDialog.  This variable is not an instance variable, since it must be
  461.                 accessible from the abortProc function.  Since the AbortProc method has no 'knowledge'
  462.                 of the printer object, it would not be able to intercept a message from the dialog.
  463.  
  464. UserAbort       A global boolean variable that is set when the user presses cancel in the print dialog box. 
  465.                 This variable is checked by the AbortProc function to see if the print job has been
  466.                 aborted.  See the discussion of the Abort Procedure near the beginning of this document.
  467.  
  468.  
  469. UNIT METHODS
  470.  
  471. AbortProc       This function is a callback function, called by windows during a print job.  If the user
  472.                 presses cancel in the Print Dialog box, this function notifies Windows that the print job
  473.                 had been canceled.
  474.  
  475.  
  476. TPRINTER METHODS
  477.  
  478. CheckNewPage           This method compares the value of posY (the vertical position of the print head
  479.                 on the page) with MaxY (the maximum page length).  If posY > maxY, the newPage
  480.                 method is called.
  481.  
  482. CheckStart      This method is responsible for some of the initialization that takes place within the printer
  483.                 object.  It's calls deal mainly with the interface between the application and the printer
  484.                 object.  The initialization between the printer object and Windows is handled by the Start
  485.                 method.  CheckStart is responsible for instantiating the print dialog and the abort
  486.                 procedure, and disabling mouse and keyboard input to the parent window.
  487.  
  488.                        If these initializations are performed successfully, CheckStart then calls the
  489.                 beginDoc method, which sends the STARTDOC escape call to the GDI.  If any of the
  490.                 Init calls fail, or if the document is not started, checkStart reverses those initializations
  491.                 that were successfully completed.
  492.  
  493. DoNewFrame      Calls the ancestor.doNewFrame method to force a form feed.
  494.  
  495. Finish          This method is called at the end of a print job.  It calls the EndOfFile method and the
  496.                 stopPrinter method, and frees the procedure instance created for the abort procedure.
  497.  
  498. Height          Returns the line spacing for the selected font.  This is the sum of the metrics.tmHeight
  499.                 and metrics.tmExternalLeading fields.  See the discussion under metrics, above, for more
  500.                 information.
  501.  
  502. Init            The Init method calls the ancestor.Init method, and initializes several instance variables.
  503.  
  504. LineWidth       This method returns the width (line length) of the passed string, based on the currently
  505.                 selected font.  This is done through a call to GetTextExtent, which returns a longInt
  506.                 value containing the height of the string in the high word, and the width of the string in
  507.                 the low word.  The lineWidth method returns only the width of the string.  This value
  508.                 is used in the print method to check if the length of the string currently being printed
  509.                 causes the current line length to exceed the width of the page.  If so, the offending text
  510.                 is wrapped to the next line.
  511.  
  512. NewAbortProc    This method calls the makeProcInstance function to obtain a far pointer to an abort
  513.                 procedure,  bound to the data segment of the current application instance.  This value is
  514.                 then passed in the inData parameter of the SetAbortProc escape call.  This function
  515.                 returns true if the escape call returns a value greater than zero.  
  516.  
  517.                        It is important to note that all this method does is pass a pointer to a function. 
  518.                 It does not guarantee that the pointer is valid, or that the function itself is valid.  For
  519.                 example, if the EXPORT statement is left off of the AbortProc declaration, the program
  520.                 will still run, but the cancel dialog box will not work.
  521.  
  522. NewLine         NewLine causes a line feed by setting the posX variable to 0, and incrementing the posY
  523.                 variable by the value returned by the height method.
  524.  
  525. NewPage         NewPage starts a new page by resetting the values of posX and posY, and calling the
  526.                 doNewFrame method.
  527.  
  528. PageSize        This method returns a tPoint type containing the size of the page in pixels.  These values
  529.                 are obtained by two calls to the Windows GetDeviceCaps method.  GetDeviceCaps 
  530.                 allows you to determine the extent of the printer device text writing abilities.  There are
  531.                 about 28 different queries that can be made, including the horizontal and vertical
  532.                 resolution (used here), the aspect ratios for line drawing, the number of fonts, and so on. 
  533.                 The possible parameters for calls to this method are listed in the Window reference
  534.                 guide, under Device capabilities.
  535.  
  536. Print           This method calls the printString method to send a line of text to the printer.  Before
  537.                 making this call, Print determines whether the line length has been exceeded, based on
  538.                 the current position of the print head and the width of the string to be printed.  If the
  539.                 length has been exceeded, Print forces a new line.  Note that the entire string is wrapped
  540.                 to the next line  from the beginning.  The string is then sent to the printer by a call to
  541.                 the printString method.  If the printString call is successful, the value of posX is
  542.                 incremented by the width of the string.  Under normal circumstances, print does not issue
  543.                 a line feed.  The Print method is similar to the Write procedure in standard pascal, in
  544.                 that it does not advance to the next line.
  545.  
  546. PrintDlg        Creates the print dialog box, a modeless dialog present while the print job is in progress. 
  547.                 The purpose of the dialog box is to allow the user to cancel a print job.
  548.  
  549. PrintLine       PrintLine is similar to the WriteLn procedure in standard Pascal. It prints a line of text,
  550.                 followed by a line feed.  It does this by calling the print method, followed by the
  551.                 newLine method.
  552.  
  553. PrintString     PrintString sends a null-terminated string of text to the selected device context using the
  554.                 TextOut method.  The TextOut method writes the character string using the currently
  555.                 selected font.  It is called by the Print method.
  556.  
  557. RemoveDlg       Destroys the print dialog box and disposes of the printDialog pointer.
  558.  
  559. ResetPos        Sets the posX and posY values to 0, indicating the start of a new page.
  560.  
  561. Start           The start method takes care of initializing the printer object and its relation with
  562.                 Windows.  It is responsible for obtaining a device context, initializing most of the
  563.                 instance variables, and calling the CheckStart method to complete the initialization.  If
  564.                 checkStart is successful, Start returns true.
  565.  
  566. StopPrinter     This method re-enables the parent window, removes the print dialog box, and sets the
  567.                 OkToPrint variable to false.
  568.  
  569. TextHeight      Returns the value of the metrics.tmHeight field, which is the height of the character cell
  570.                 of the currently selected font.
  571.  
  572. TextWidth       Returns the average width of the characters in the currently selected character set.
  573.  
  574. Using the tPrinter object
  575.         The tPrinter object is the object to use in an application to obtain basic access to the printer.  The
  576. sample program TestPrn1 illustrates a simple file printing program that uses this object.  Tprinter handles
  577. the interface between an application and the tPrnDevice object.  That object handles the interface between
  578. the tPrinter object and the GDI.  By maintaining this separation, it becomes much easier to extend the
  579. functionality of either the tPrnDevice or the tPrinter.  If you find that the two objects meet your basic
  580. printing needs, but you need more features than they offer, the correct way to add new features is to
  581. create descendant objects.  You should avoid, as much as possible, modification of these parent objects.
  582.  
  583.  
  584. ===============================================
  585.                                             SAMPLE PROGRAMS
  586. The TestPrn1 sample program
  587.         This program allows you to print a file or display the device mode dialog for a printer.  The
  588. application object is tTestApp, the main window object is tPrnWindow.  The printer object is instantiated
  589. using the tPrnWindow instance variable aPrinter.  The two methods that interest us are the setPrinter
  590. method and the filePrint method.
  591.  
  592. SETPRINTER METHOD
  593.  
  594.         This method displays the device mode dialog.  It consists of three lines:
  595.                 aPrinter := new(pPrinter,Init(hInstance,@self));
  596.                 aPrinter^.prnDeviceMode(hWindow);
  597.                 dispose(aPrinter,Done);
  598.  
  599.         The first line instantiates aPrinter as a printer object.  The hInstance variable contains the current
  600. tTestApp instance. The @Self parameter is the tPrnWindow object.  The second line calls the
  601. prnDeviceMode method of the printer object.  The hWindow parameter contains the device context for
  602. the current tPrnWind. The third disposes of the printer object. In the printer object, the device mode call
  603. has been set to change the environment only for the current session.
  604.  
  605.  
  606. THE FILEPRINT METHOD
  607.  
  608.         The filePrint method first displays a dialog box to get the name of a file to print.  If the file name
  609. is invalid, the method does nothing. (After all, this is to demonstrate printing!).  Once a valid file name
  610. is received, we are ready to print.  As in the SetPrinter method, an instance of the tPrinter object is
  611. created by the line
  612.                 aPrinter := new(pPrinter, Init(hInstance, @self)
  613.         here, the hInstance represents the current instance of tTestApp. @self represent the tPrnWindow
  614. instantiated by tTestApp.  The value of these two parameters will be used in the instantiation of the
  615. AbortProc method and the print dialog box.  The next line calls the Start method:
  616.                 if aPrinter^.Start('PrnTest',hWindow) then ...
  617.         here, 'PrnTst' is the document name that will be used to uniquely identify this document, and
  618. hWindow is the current device context (tPrnWindow).  This function call must return true in order for
  619. printing to commence.  If it returns false, an error message should display, indicating what problem
  620. occurred.
  621.  
  622.         Once the document has been started, the application can send continuous output to the printer. 
  623. This is represented by the WHILE..DO loop, following the call to the Start method.  This loop reads in
  624. the next line of text from the file, and calls the printLine method, passing a pointer to the string as a
  625. parameter.
  626.  
  627.         When the end of textFile has been reached, the Finish method is called to end the print job.  This
  628. call results in the print buffer being flushed, and a NewFrame call to advance the page.  Finally, the
  629. aPrinter variable is disposed, and the job is done.
  630.  
  631.         While the print job is in progress, you should notice several things.  First, the parent window to
  632. which the print dialog belongs has been disabled.  Mouse and keyboard input to that window have been
  633. inhibited.  It is still possible to access other windows on the screen, but the only active portion of the
  634. print process is the cancel dialog box.  Another thing to notice is that the printing does not start
  635. immediatly.  In fact, for smaller files, printing won't start until you have returned to the application. 
  636. This is because Windows and the print manager spool the output to disk or memory, prior to starting
  637. output.  Finally, note that any changes you make to the printer parameters affect only the current print
  638. session (if the driver was written for Windows 3).
  639.  
  640.  
  641.                                        THE REPORTPRINTER OBJECT
  642.  
  643.         The reportPrinter object descends from the tPrinter object.  It does not modify any of tPrinter's
  644. methods.  Rather, it adds some behaviors to tPrinter to change the physical appearance of a printed
  645. document.  ReportPrinter adds user defined pagination, based on lines per page, and headings that are
  646. printed on the top of each page.  In the full implementation of this object in another system, it also
  647. printed column headings, and a columnar report with totals.
  648.  
  649.         The point of the reportPrinter object is to illustrate how the behavior of an object is modified by
  650. a descendant.  It is intended as an example, not a full implementation of an application. 
  651.  
  652.  
  653.                                             TREPORTPRINTER 
  654.  
  655. INSTANCE VARIABLES
  656.  
  657. Heading1        Holds the first heading line.
  658. Heading2        Holds the second heading line.
  659. lineCount       Tracks how many lines have been printed.  This variable is used to determine when to
  660.                 advance to a new page.  Note that, in this implementation, if a line is wrapped by the
  661.                 Print method, line count is not affected.
  662. maxLines        Maximum number of lines per page.  This variable is set in the Init method.
  663. pageCount       Keeps track of the page number.  This count is included in heading line 2.
  664. rDateTime       Holds the run date and time of the report.  This value is included in heading line 2.
  665.  
  666.  
  667. METHODS
  668.  
  669. Init            Calls the ancestor(tPrinter) Init method to set the application instance and parent values. 
  670.                 Init then initializes several other of the reportPrinter instance variables.
  671.  
  672. SetDefaults     SetDefaults initializes the values of the two heading lines.  Heading1 is set to the value
  673.                 of the passed parameter, heading2 is set to the run date and time by a call to
  674.                 setHeading2.
  675.  
  676. SetHeading2     Initializes heading2 to the run date and time instance variable and the constant PAGE.
  677.  
  678. SetPageNum      Strings the current page count into the local variable pCount, then copies it into the
  679.                 proper position in heading2.
  680.  
  681. printMainHeadings      Calls setPageNum, then sends heading1 and heading2 to the printer.
  682.  
  683. StartNewPage    Page break.  If the page is greater than zero, calls new page, then prints the headings. 
  684.                 NewPage causes a form feed.  In this version of the object, pageCount is initially set to
  685.                 zero.  This routine does not issue a form feed for the first page. (I was running out of
  686.                 paper!)
  687.  
  688. OutText         Check lineCount against maxLines to see if it is time for a new page.  If so,
  689.                 startNewPage is called.  The text passed as a parameter is sent to the printer with the
  690.                 tPrinter.PrintLine method, and pageNum in incremented by 1.
  691.  
  692. SetRunTime      A method to get the current date and time, format them, and copy them to the rDateTime
  693.                 instance variable.
  694.  
  695.  
  696.                                         SAMPLE PROGRAM TESTPRN2
  697.         This application is an example of the use of the reportPrinter object.  The majority of the code
  698. is identical to that in TestPrn1, so I will not repeat that discussion.  One difference is that the
  699. tPrnWindow instance variable aPrinter is now declared to be of type reportPrinter.  The other differences
  700. are in the instantiation of aPrinter in the body of the program, and the object methods that are called
  701. during execution.  
  702.  
  703.                 In the method filePrint, the init call for aPrinter now includes an addition parameter, that
  704. for the lines per page.  This value sets the reportPrinter instance variable maxLines, which is checked
  705. each time the outText method is called.  Next, the reportPrinter method setDefaults is called to initialize
  706. the headings.  The default heading passes is simply the word heading.  If a nil value is passes, an empty
  707. string will be printer for heading1.  The second heading line will be the run date and time, and the page
  708. number.  The start method is called to begin the document.  As the file is read, text is sent to the
  709. reportPrinter method outText, rather that the printLine method as was done in the previous example. 
  710. This is so reportPrinter can manage the lines per page.  The balance of the execution is identical to the
  711. testPrn1 program.  
  712.  
  713.  
  714.                                          WINDOWS PRINTING TIPS
  715.         Keep in mind that tPrinter handles text on a line basis; that is, an application sends ready to print
  716. lines to the print or printLine methods.  Remember that these are printer objects.  Their only purpose is
  717. to get information from the application to the printer.  If the output must be formatted in a special
  718. manner, it should be done before the print or printLine methods are called.  That is the technique used
  719. in the original implementation of reportPrinter.  The reportPrinter object formatted the text in to columns,
  720. formatted the subtotals and totals received from other objects, and passed a complete line of text to the
  721. tPrinter object.  The overriding reason for doing this is that you know for certain that if an error in
  722. formatting text occurs, it occurs before the line gets to the printer unit.  It makes report creation and
  723. debugging infinitely easier.  Remember also that object-oriented programming takes the black box concept
  724. several steps farther.  Any object descended from the printer object should concern itself only with
  725. printing.  Any tasks not required for formatting a line, or printing it out do not belong in a printer object.
  726.  
  727. ENDIT
  728.         I sincerely hope that these objects, examples and documentation prove useful to your quest for
  729. the perfect Windows application.  I have learned a great deal about Windows programming while creating
  730. them.  I look forward to corresponding with you on the Compuserve forum.  Happy Computing!
  731. -Bob Galivan